package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"sync"

	"github.com/jacobsa/go-serial/serial"
)

//串口类型
const TYPE_SERIAL = "serial"

//网络类型
const TYPE_NET = "network"

//高试模式设置
var DBG = false

var WAIT sync.WaitGroup

//任务取消标志
var IS_CANNEL = false

//ip地址
type NetOption struct {
	IP   string
	Port int
}

//pipe对象
type PipeIO struct {
	IO      io.ReadWriteCloser
	Dev     string
	IsClose bool
}

//关闭io并设置关闭标志
func (p *PipeIO) Close() {
	if p.IO != nil {
		p.IO.Close()
		p.IsClose = true
	}
}

//按照配置打开串口
func NewSerial() (PipeIO, error) {
	serial_io := PipeIO{
		Dev:     TYPE_SERIAL,
		IsClose: true,
	}
	ser_io, err := serial.Open(AppConfig.Serial_Opt)
	if err != nil {
		log.Printf("串口%v打开错误:%v\n", AppConfig.Serial_Opt.PortName, err.Error())
		serial_io.IO = nil
	} else {
		log.Printf("串口已打开，串口名: %s; 波特率: %d\n", AppConfig.Serial_Opt.PortName, AppConfig.Serial_Opt.BaudRate)
		serial_io.IO = ser_io
		serial_io.IsClose = false
	}
	return serial_io, err
}

/**
func (rw *io.ReadWriteCloser) WriterTo(dst io.ReadWriteCloser) (written int64, err error) {

}
**/
func Pipe(data_to *PipeIO, data_frm *PipeIO, wt *sync.WaitGroup) {
	defer data_to.Close()
	defer data_frm.Close()
	defer wt.Done()

	_, err := DataCopy(data_to, data_frm)
	if err != nil {
		log.Printf("透传错误，请检查网络及设备连接:%v\n", err.Error())
	}
}

//内存数据复制
func DataCopy(p_to *PipeIO, p_frm *PipeIO) (written int64, err error) {
	buf := make([]byte, 32*1024)
	for {
		//判断任务是否取消
		if IS_CANNEL {
			break
		}
		nr, er := p_frm.IO.Read(buf)
		if nr > 0 {
			nw, ew := p_to.IO.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				//发送错误通知
				IS_CANNEL = true
				p_frm.IO.Write([]byte(fmt.Sprintf("%s write error...", p_to.Dev)))
				break
			}
			if nr != nw {
				err = io.ErrShortWrite
				break
			}
		}
		if DBG {
			log.Printf("透传数据[%s--->%s]: %v\n", p_frm.Dev, p_to.Dev, string(buf[0:nr]))
		}
		if er == io.EOF {
			break
		}
		if er != nil {
			err = er
			//发送错误通知
			IS_CANNEL = true
			p_to.IO.Write([]byte(fmt.Sprintf("%s read error...", p_frm.Dev)))
			break
		}
	}
	return written, err
}

func main() {
	//读取设置
	if !Read_config() {
		return
	}
	flag.BoolVar(&DBG, "debug", false, "是否开启调试模式，调试模式会输出一些运行中信息")
	flag.Parse()

	serial_pipe, err := NewSerial()
	if err != nil {
		return
	}
	nlistener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", AppConfig.Net_Opt.IP, AppConfig.Net_Opt.Port))
	if err != nil {
		log.Printf("网络监听错误，请检查端口是否被占用或无权限:%v\n", err.Error())
		return
	}
	net_pipe := PipeIO{
		Dev:     TYPE_NET,
		IsClose: false,
	}
	for {
		IS_CANNEL = false
		if serial_pipe.IsClose {
			serial_pipe, err = NewSerial()
			if err != nil {
				break
			}
		}
		log.Printf("网络正在监听[IP:%s:%d]......", AppConfig.Net_Opt.IP, AppConfig.Net_Opt.Port)
		n_conn, err := nlistener.Accept()
		if err != nil {
			log.Printf("网络连接错误:%v\n", err.Error())
			continue
		}
		net_pipe.IO = n_conn
		net_pipe.IsClose = true
		WAIT.Add(2)
		go Pipe(&serial_pipe, &net_pipe, &WAIT)
		go Pipe(&net_pipe, &serial_pipe, &WAIT)
		WAIT.Wait()
	}
}
